/**************************************************************/
/******************* mysql常用（基础）方法 *********************/
/**************************************************************/

const { standTables } = require("../../../../../../app/tables-data");
const { getSomeFieldNames, getStandFieldNames } = require("../../../compile/mysql/_utils");
const { N, typeOf, needParam, consoleLog } = require("../../../utils");

module.exports = {
  /**
   * 获取由数组拼接而成的字段字符串
   * @param {string[]} fields 字段
   */
  // getFieldsStr(fields = ["*"]) {
  //   return fields.join(", ");
  // },
  /**
   * 获取mysql的注释文本
   * @param {string} str 文本内容
   * @returns {string}
   */
  getAnnoStr(str = "") {
    return `-- ${str}`;
  },
  /**
   * 判断是否是mysql的变量名称 【待完善】
   * @param {any} str 要检测的字符串
   */
  getIsSqlVar(str, startsWith = false) {
    if (typeof str !== "string") return false;
    // const baseVars =['SELECT','FROM','WHERE','AND','OR','IN','NOT IN','ORDER'];
    // const commonVars=['BETWEEN'];
    // return ["CURRENT_TIMESTAMP", "LIKE", "REGEXP", "NOT"].includes(str.toUpperCase());
    const words = ["CURRENT_TIMESTAMP", "LIKE", "REGEXP", "NOT"]; // not status
    if (!startsWith) return words.includes(str.toUpperCase());
    return words.find(word => str.toUpperCase().startsWith(word));
  },
  /**
   * 判断是否是日期时间 【待完善】
   * @param {string} name 字段名称
   * @returns {boolean}
   */
  getIsDateTime(name = "") {
    return name.includes("time");
  },
  /**
   * 获取数据库查询时的 where字符串
   * @param {string|boolean|array|object} where 为字符串时，可写入原生mysql语句，为数组时，表示或者（OR），为对象时，表示且（AND）
   * @param {string} tableName 表名称
   * @param {string} nChar 换行符（链接符号）
   * @param {string|string[]} 表的所有属性名
   * @example 为string时， 例：id = 10
   * @example 为boolean时， 例：true
   * @example 为object时， 如下所示：
   * 运算符号可能值：not, in, like, /^a.+a$/, >, =, <, >=, ！=, <=
   * 注意事项：运算符号为英文关键字变量时，可小写大写无所谓
   * 特殊运算符号说明
   * @produce LIKE % 表示任意0个或多个字符。若匹配中文，有时需使用两个百分号（%%），_ 表示任意单个字符。例： '%张%'（包含张）、'abc%'（以abc开头）、'_o%'（第二个字符是o）
   * @produce REGEXP 正式表达式。例：'^a[0-9]+'（以a开头且后面跟着一个或多个数字的记录）
   * {
   *   id: 1, // id等于1
   *   'pId:!=': 2,  // pId不等于2
   *   type: [1,2], // type为1或2之一
   *   'classify|not': [3, 4], // classify不为3和4之一
   *   'status|not': 0, // 给0和1取反 UPDATE operate_status SET status = NOT status WHERE create_id = 1;
   *   'age|between': [18, 25], // age在18~25之间（含18和25）
   *   'name|%like%': '李', // 名字中含有李字
   *   'user_name,nickname|%like%': '李', // user_name或nickname字段中含有李字
   *   'address|like%': '四川省', // address以四川省开头
   *   'char|_like%': 'o', // char的第二个字母为o
   *   str|/[a-z]+/': 'abcDef' // str匹配小写字母
   * }
   * @example 为array时，例：[{}, {}]，其中每一个对象均为上述的对象规则
   * @notice 为对象时，表示且（AND）；为数组时，表示或（OR）
   */

  getWhereStr(where = "", allFieldNames = needParam(), tableName, nChar = N) {
    const wT = typeOf(where);
    if (["String", "Boolean"].includes(wT)) return where;
    if (wT === "Object") {
      const keys = Object.keys(where);
      if (!keys.length) return "";
      const list = keys.map(modKey => {
        let val = where[modKey];
        const vT = typeOf(val);
        let [key, mod = vT === "Array" ? "IN" : "="] = modKey.split("|");
        const isMultiProp = key.includes(",");
        if (allFieldNames?.length && (allFieldNames.includes(key) ? false : !isMultiProp)) return "";
        mod = mod.toUpperCase();
        if (mod === "NOT") mod = "NOT IN";
        if (vT === "Array") {
          if (mod === "BETWEEN") {
            let [min, max] = val;
            if (module.exports.getIsDateTime(key)) {
              if (!min.includes(" ")) min += ` 00:00:00`;
              if (!max.includes(" ")) max += ` 23:59:59`;
            }
            val = `'${min}' AND '${max}'`;
          } else {
            val = `(${val.map(it => `'${it}'`).join(", ")})`;
          }
        } else if (vT === "String") {
          // 如果是模糊匹配
          if (mod.includes("LIKE")) {
            if (mod === "LIKE") {
              val = `'${val}'`;
            } else {
              val = `'${mod.replace("LIKE", val)}'`;
              mod = "LIKE";
            }
          }
          // 如果是正则表达式匹配
          else if (/^\/.+\/$/.test(mod)) {
            val = `'${mod.slice(1, -1)}'`;
            mod = "REGEXP";
          } else {
            if (!module.exports.getIsSqlVar(mod)) {
              val = `'${val}'`;
            }
          }
        }
        if (vT === "Undefined") return "";
        if (isMultiProp) {
          const props = key.split(",").filter(it => (allFieldNames?.length ? allFieldNames.includes(it) : true));
          const str = props
            .map(key => {
              if (tableName && !key.includes(".")) key = `${tableName}.${key}`;
              return `${key} ${mod} ${val}`;
            })
            .join(" OR ");
          return props.length > 1 ? `(${str})` : str;
        } else {
          if (tableName && !key.includes(".")) key = `${tableName}.${key}`;
          return `${key} ${mod} ${val}`;
        }
      });
      const filterList = list.filter(it => !!it);
      if (!filterList.length) return "";
      return filterList.join(`${nChar}AND `);
    }
    if (wT === "Array") {
      const list = where
        .map(it => {
          const iT = typeOf(it);
          if (iT !== "Object") throw new Error(`暂未处理此类型：${iT}`);
          return module.exports.getWhereStr(it, allFieldNames, tableName, nChar);
        })
        .filter(it => !!it);
      if (!list.length) return "";
      return list.map(it => it).join(`${N}OR `);
    }
    throw new Error(`暂未处理此种类型：${wT}`);
  },
  /**
   * 获取所有字段名称
   */
  getTableFieldNames(tableName = needParam()) {
    const target = standTables[tableName];
    if (!target) throw new Error(`不存在该表：${tableName}`);
    return target.sqlFields;
  },
  /**
   * 获取表中包含的字段名称
   * @param {string} tableName 表名称
   * @param {string[]} names 外部传入的字段名称
   */
  getOwnerFieldNames(tableName = "", names = []) {
    const allNames = module.exports.getTableFieldNames(tableName);
    return names.filter(name => allNames.includes(name));
  },
  /**
   * 获取表中不包含的字段名称
   * @param {string} tableName 表名称
   * @param {string[]} names 外部传入的字段名称
   */
  getOmitFieldNames(tableName = "", names = []) {
    const allNames = module.exports.getTableFieldNames(tableName);
    return names.filter(name => !allNames.includes(name));
  },
  /**
   * 获取要插入的表格数据（rows）
   * @param {string} tableName 表格名称
   * @param {object} params 要插入的数据
   */
  getInsertRows(tableName = needParam(), params = needParam()) {
    const { sqlFields } = standTables[tableName];
    const allNames = sqlFields.map(it => it.name);
    const requiredNames = allNames.filter(it => it.notNull);
    const names = Object.keys(params);
    const rows = [[], []];
    allNames.forEach(name => {
      if (names.includes(name)) {
        rows[0].push(name);
        rows[1].push(params[name]);
      } else if (requiredNames.includes(name)) {
        rows[0].push(name);
        const defVal = sqlFields.find(it => it.name === name).defaultValue;
        rows[1].push(defVal);
      }
    });
    return rows;
  },
  /**
   * 获取要更新的数据(obj)
   * @param {string} tableName 表格名称
   * @param {object} params 要插入的数据
   * @param {number} update_id 修改人id
   */
  getUpdateParams(tableName = needParam(), params = needParam(), update_id = needParam()) {
    const args = {};
    const { sqlFields } = standTables[tableName];
    const allNames = sqlFields.map(it => it.name);
    const names = Object.keys(params);
    // 暂时不处理必填项字段未传的情况了。因为一般来说，创建的时候，已经保证数据正确了，修改时候不会存在必填项没填的情况
    names.forEach(key => {
      if (allNames.includes(key)) args[key] = params[key];
      // const [name, mod] = key.split("|");
      // if (allNames.includes(name)) {
      //   const val = params[key];
      //   if (!mode) {
      //     args[name] = val;
      //   } else if (mod.toUpperCase() === "NOT") {
      //     if ([0, 1].includes(Number(val))) {
      //       args[name] = `NOT ${name}`;
      //     } else {
      //       args[name] = val;
      //     }
      //   }
      // }
    });
    if (!args.update_time && allNames.includes("update_time")) args.update_time = "CURRENT_TIMESTAMP";
    if (!args.update_id && allNames.includes("update_id")) args.update_id = update_id;
    return args;
  },
  /**
   * 获取标签str
   */
  getTagsStr(tableName = needParam(), alias = "tags") {
    return `IFNULL(
    (
      SELECT JSON_ARRAYAGG(JSON_OBJECT('id', t.id,'label', t.name))
      FROM tag t WHERE JSON_CONTAINS(${tableName}.tags, CAST(t.id AS JSON), '$')
    ), JSON_ARRAY()) AS ${alias}`;
  },
};
